From: Mike FABIAN Date: Tue, 17 Jun 2025 13:48:42 +0000 (+0200) Subject: status/keyboard: Limit the input method indicator to < 3 grapheme clusters X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~124^2^2~40 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=f2246442a0ade263a1f9e775f02818bbd2862ae8;p=gnome-shell.git status/keyboard: Limit the input method indicator to < 3 grapheme clusters The Javascript string length is not suitable for measuring the approximate length of a rendered text in the input method indicator. Most single code point emoji like `๐Ÿš€ U+1F680` get length 2 already, adding a variation selector to make the emoji display in black and white like `๐Ÿš€๏ธŽ U+1F680 U+FE0E` makes the length 3. More complicated emoji sequences may get much longer lengths even though the still display as one character. For example `๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง U+1F469, U+200D, U+1F469, U+200D, U+1F467, U+200D, U+1F467` gets length 11. This is not only the case for emoji but also for other text, for example the Korean jamo `แ„€แ…กแ†จ U+1100, U+1161, U+11A8` display as only 1 glyph but the Javascript length is 3. A better way to measure the approximate length of a rendered text is to count the Unicode grapheme clusters in the text using Intl.Segmenter. This counts all the examples above as 1 grapheme cluster each. There seem to be some bugs in the Intl.Segmenter, for example `เค•เฅเคทเคฟ U+0915, U+094D, U+0937, U+093F` is counted as 2 grapheme clusters, the correct result should be 1. But it is good enough to measure the approximate length of the input method indicator and certainly better than the Javascript string length. Resolves: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1026 Part-of: --- diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js index 6971ce128..c4779d4eb 100644 --- a/js/ui/status/keyboard.js +++ b/js/ui/status/keyboard.js @@ -998,6 +998,12 @@ class InputSourceIndicator extends PanelMenu.Button { } } + _getGraphemeClusters(text = '') { + const segmenter = new Intl.Segmenter(undefined, {granularity: 'grapheme'}); + const segments = [...segmenter.segment(text)].map(o => o.segment); + return segments; + } + _buildPropSubMenu(menu, props) { if (!props) return; @@ -1021,7 +1027,8 @@ class InputSourceIndicator extends PanelMenu.Button { let currentSource = this._inputSourceManager.currentSource; if (currentSource) { let indicatorLabel = this._indicatorLabels[currentSource.index]; - if (text && text.length > 0 && text.length < 3) + const graphemeClusters = this._getGraphemeClusters(text); + if (graphemeClusters.length > 0 && graphemeClusters.length < 3) indicatorLabel.set_text(text); } }